﻿using System;
using System.Reactive.Linq;
using Avalonia.Media;
using Avalonia.Controls.Shapes;
using Avalonia.Input;

namespace KinonekoSoftware.Foundation.UX.Controls
{
    public class GeometryDegreeControl : Panel
    {

        public static readonly StyledProperty<int>      StrokeThicknessProperty;
        public static readonly StyledProperty<IBrush>   StrokeProperty;
        public static readonly StyledProperty<int>      IconGapProperty;
        public static readonly StyledProperty<Geometry> IconProperty;
        public static readonly StyledProperty<int>      IconSizeProperty;
        public static readonly StyledProperty<int>      ValueProperty;
        public static readonly StyledProperty<int>      MaximumProperty;

        static GeometryDegreeControl()
        {
            StrokeThicknessProperty = AvaloniaProperty.Register<GeometryDegreeControl, int>(nameof(StrokeThickness), 1);
            StrokeProperty          = AvaloniaProperty.Register<GeometryDegreeControl, IBrush>(nameof(Stroke));
            IconGapProperty         = AvaloniaProperty.Register<GeometryDegreeControl, int>(nameof(IconGap));
            IconProperty            = AvaloniaProperty.Register<GeometryDegreeControl, Geometry>(nameof(Icon));
            IconSizeProperty        = AvaloniaProperty.Register<GeometryDegreeControl, int>(nameof(IconSize), 13);
            ValueProperty           = AvaloniaProperty.Register<GeometryDegreeControl, int>(nameof(Value));
            MaximumProperty         = AvaloniaProperty.Register<GeometryDegreeControl, int>(nameof(Maximum));

            StrokeThicknessProperty.Changed.Subscribe(x => OnInvalidatePath((GeometryDegreeControl)x.Sender));
            StrokeProperty.Changed.Subscribe(x => OnInvalidatePath((GeometryDegreeControl)x.Sender));
            IconGapProperty.Changed.Subscribe(x => OnInvalidatePath((GeometryDegreeControl)x.Sender));
            IconProperty.Changed.Subscribe(x => OnInvalidatePath((GeometryDegreeControl)x.Sender));
            IconSizeProperty.Changed.Subscribe(x => OnInvalidatePath((GeometryDegreeControl)x.Sender));
            ValueProperty.Changed.Subscribe(x => OnInvalidatePath((GeometryDegreeControl)x.Sender));
            MaximumProperty.Changed.Subscribe(x => OnInvalidateRenderData((GeometryDegreeControl)x.Sender));
        }

        private static void OnInvalidateRenderData(GeometryDegreeControl c)
        {
            c.GenerateElement();
        }

        private static void OnInvalidatePath(GeometryDegreeControl c)
        {
            if (c.Children.Count == 0)
                c.GenerateElement();
            c.SyncElement();
        }

        /*
         *
        protected const string Thumb = "F1 M24,24z M0,0z M20.84,4.61A5.5,5.5,0,0,0,13.06,4.61L12,5.67 10.94,4.61A5.5,5.5,0,0,0,3.16,12.39L4.22,13.45 12,21.23 19.78,13.45 20.84,12.39A5.5,5.5,0,0,0,20.84,4.61z";
        protected const string Heart = "F1 M24,24z M0,0z M20.84,4.61A5.5,5.5,0,0,0,13.06,4.61L12,5.67 10.94,4.61A5.5,5.5,0,0,0,3.16,12.39L4.22,13.45 12,21.23 19.78,13.45 20.84,12.39A5.5,5.5,0,0,0,20.84,4.61z";
        protected const string Rate  = "F1 M24,24z M0,0z M12,2L12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26 12,2z";
         */

        public int Maximum
        {
            get => GetValue(MaximumProperty);
            set => SetValue(MaximumProperty, value);
        }

        public int Value
        {
            get => GetValue(ValueProperty);
            set => SetValue(ValueProperty, value);
        }

        private void OnElementClick(object sender, PointerPressedEventArgs e)
        {
            var p     = (Border)sender;
            var index = (int)p.Tag;

            if (Value == 1)
            {
                Value = 0;
            }
            else
            {
                Value = index;
            }
            HighlightElement();
        }

        protected virtual Border GenerateElement(bool isFirst, bool isLast, int index, double size)
        {
            var path = new Avalonia.Controls.Shapes.Path
            {
                Data                = Icon,
                Width               = size,
                Height              = size,
                UseLayoutRounding   = true,
                Stretch             = Stretch.Uniform,
                Tag                 = index,
            };

            var bd = new Border
            {
                Background = new SolidColorBrush(Colors.Transparent),
                Child      = path,
                Width      = size,
                Height     = size,
                Tag        = index,
            };


            bd.PointerPressed   += OnElementClick;

            return bd;
        }

        private void SyncElement()
        {
            var thickness = Math.Clamp(StrokeThickness, 1, 500);
            var size      = Math.Clamp(IconSize, 9, 512);
            for (var i = 0; i < Children.Count; i++)
            {
                if (Children[i] is not Border { Child: Avalonia.Controls.Shapes.Path p } b)
                {
                    continue;
                }

                b.Width  = size;
                b.Height = size;
                p.Data   = Icon;
                p.Width  = size;
                p.Height = size;

                if (Value - 1 >= i)
                {
                    p.Fill            = Stroke;
                    p.StrokeThickness = 0;
                    p.Stroke          = null;
                }
                else
                {
                    p.Fill            = XamlCore.Transparent;
                    p.StrokeThickness = thickness;
                    p.Stroke          = Stroke;
                }
            }
        }

        protected void GenerateElement()
        {
            var count = Math.Clamp(Maximum, 0, 100);
            var size  = Math.Clamp(IconSize, 9, 512);

            //
            // Clear
            ClearElementImpl();

            if (count == 0)
            {
                return;
            }

            GenerateElementImpl(count, size);
            InvalidateMeasure();
            HighlightElement();
        }

        private void ClearElementImpl()
        {
            for (var i = 0; i < Children.Count; i++)
            {
                if (Children[i] is not Border { Child: Avalonia.Controls.Shapes.Path p } b)
                {
                    continue;
                }

                b.PointerPressed -= OnElementClick;
            }

            Children.Clear();
        }

        private void GenerateElementImpl(int count, int size)
        {

            if (count >= 1)
            {
                Children.Add(GenerateElement(true, false, 1, size));
            }

            if (count == 2)
            {
                Children.Add(GenerateElement(false, true, 2, size));
            }
            else
            {
                for (var i = 1; i < count; i++)
                {
                    Children.Add(GenerateElement(false, i == count - 1, i + 1, size));
                }
            }
        }

        protected void HighlightElement()
        {
            var thickness = Math.Clamp(StrokeThickness, 1, 500);

            if (Children.Count == 0 && Children.Count < Maximum)
            {
                return;
            }

            for (var i = 0; i < Maximum; i++)
            {
                if (Children[i] is not Border { Child: Avalonia.Controls.Shapes.Path p })
                {
                    continue;
                }

                if (Value - 1 >= i)
                {
                    p.Fill            = Stroke;
                    p.StrokeThickness = 0;
                    p.Stroke          = null;
                }
                else
                {
                    p.Fill            = XamlCore.Transparent;
                    p.StrokeThickness = thickness;
                    p.Stroke          = Stroke;
                }
            }
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            return new Size(Maximum * IconSize + (Maximum - 1) * IconGap, IconSize);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            var x = 0d;

            foreach (var element in Children)
            {
                element.Arrange(new Rect(x, 0, IconSize, IconSize));
                x = x + IconGap + IconSize;
            }

            return base.ArrangeOverride(finalSize);
        }

        public int IconSize
        {
            get => GetValue(IconSizeProperty);
            set => SetValue(IconSizeProperty, value);
        }

        public Geometry Icon
        {
            get => GetValue(IconProperty);
            set => SetValue(IconProperty, value);
        }

        public int IconGap
        {
            get => GetValue(IconGapProperty);
            set => SetValue(IconGapProperty, value);
        }

        public IBrush Stroke
        {
            get => GetValue(StrokeProperty);
            set => SetValue(StrokeProperty, value);
        }

        public int StrokeThickness
        {
            get => GetValue(StrokeThicknessProperty);
            set => SetValue(StrokeThicknessProperty, value);
        }
    }
}